﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using HttpPackage;

namespace HTMLPackage
{
    public class HtmlDocRegEx:IHTMLDoc
    {
        private string _doc;
        private Uri _url;
        private IList<Uri> _links;

        public HtmlDocRegEx(string url)
        {
            Uri u;
            try
            {
                u = new Uri(url);
                
            }
            catch(UriFormatException e)
            {
                throw;
            }

            PageRetriever pr = new PageRetriever(u);

            this._doc = pr.GetPage();
            this._url = u;
        }
        
        public string Document
        {
            get { return this._doc; }
        }

        public Uri URL
        {
            get { return this._url; }
            set { this._url = value; }
        }

        public IList<Uri> GetAllLinks()
        {
            this._links = new List<Uri>();
            string regexpHref = "<a\\shref\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>\\S+))";
            string regexpFrameSrc = "<FRAME\\ssrc\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>\\S+))";
            Regex a = new Regex(regexpHref, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            Regex f = new Regex(regexpFrameSrc, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            MatchCollection mc1 = a.Matches(this._doc);
            MatchCollection mc2 = f.Matches(this._doc);
            
            foreach (Match m1 in mc1)
            {
                string link = m1.Groups[1].ToString();
                link = QualifyURI(link);
                this._links.Add(new Uri(link));
                
            }
            foreach (Match m2 in mc2)
            {
                string link = m2.Groups[1].ToString();
                link = QualifyURI(link);
                this._links.Add(new Uri(link));
            }
            return this._links;
        }

        public bool Contains(string searchString)
        {
            return Regex.IsMatch(this._doc, ""); 
        }

        private string QualifyURI(string link)
        {
            link = link.ToLower();
            
            if(link.StartsWith("http://"))
            {
                // fully qualified uri
                return link;
            }
            else if(link.StartsWith(".."))
            {
                // relative link

                // Get the base link
                string baseLink = GetBaseLink();

                // Define delimiter for relative link to be split
                string[] splitter = {".."};

                // Split the relative link into parts
                string[] linkParts = link.Split(splitter, StringSplitOptions.None);

                // the number of elements - 1 is the amount of levels we must jump back from current possition
                int jumpBack = linkParts.Length - 1;
                // remove trailing slash (/)
                baseLink = baseLink.Remove(baseLink.Length - 1);
                // Remove as many levels as needed
                for(int i = 0; i < jumpBack; i++)
                {
                    baseLink = baseLink.Remove(baseLink.LastIndexOf("/") + 1);
                }
                // Link relative to current baselink
                link = linkParts.Last();
                // Construct new link and return
                return baseLink + link;
            }
            else if (link.Count((f => f == '.')) == 0 || (link.Count((f => f == '.')) == 1 && HasExtension(link)))
            {
                //relative link
                string baseLink = GetBaseLink();
                if(link.StartsWith("/"))
                {
                    return baseLink + link.Substring(1);
                }
                else
                {
                    return baseLink + link;
                }
            }
            else if(link.Count((f => f == '.')) == 1 && !HasExtension(link))
            {
                //not a relative link (targets folder on specific host)
                return "http://" + link;
            }
            else if (link.Count((f => f == '.')) > 1)
            {
                // not a relative link, just needs http:// as prefix
                return "http://" + link;
            }
            else
            {
                // cannot qualify link
                return "";
            }
        }

        private bool HasExtension(string link)
        {
            bool hasExt = false;
            string[] webextensions = { ".htm", ".html", ".xhtm", ".xhtml", ".asp", ".aspx", ".php", ".jhtml" };

            foreach (string ext in webextensions)
            {
                if (link.EndsWith(ext))
                {
                    hasExt = true;
                }
            }

            return hasExt;
        }

        private string GetBaseLink()
        {
            string baseLink = string.Empty;
            if(HasExtension(_url.AbsoluteUri))
            {
                int oldLength = _url.AbsoluteUri.Length;
                int subLength = _url.Segments[_url.Segments.Length - 1].Length;
                int newLength = oldLength - subLength;

                baseLink = _url.AbsoluteUri.Remove(newLength);
            }
            else if (!_url.AbsoluteUri.EndsWith("/"))
            {
                baseLink = _url.AbsoluteUri + "/";
            }
            else
            {
                baseLink = _url.AbsoluteUri;
            }

            return baseLink;
        }
    }
}
